﻿using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Geometry;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using PlanningGIS.Util.Log;
using PlanningGIS.Util.Win32;
using PlanningGIS.ArcGIS;
using System.Diagnostics;
using System.Windows.Forms;
using PlanningGIS.Util.Management;

namespace PlanningGIS.DataConvert
{
    /// <summary>
    /// CAD转换类，将esri对象转成cad对象
    /// </summary>
    public class ConvertCAD
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public ConvertCAD()
        {

        }

        /// <summary>
        /// CAD文档处理对象
        /// </summary>
        public IAcadDocument CadDocument { get; set; }

        /// <summary>
        /// AutoCAD应用程序对象
        /// </summary>
        public IAcadApplication CadApplication { get; protected set; }

        /// <summary>
        /// 已创建好的图层名称集合
        /// </summary>
        protected List<string> _LayerNames = null;

        #region Cad基本操作

        /// <summary>
        /// 创建cad图层
        /// </summary>
        /// <returns></returns>
        protected string CreateCadLayer(string layername)
        {
            if (_LayerNames == null) _LayerNames = new List<string>();
            if (_LayerNames.Contains(layername.ToUpper())) return layername;
            AcadLayer lyr = null;
            try
            {
                lyr = CadDocument.Layers.Add(layername);
                _LayerNames.Add(layername.ToUpper());
                return layername;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("创建CAD图层" + lyr + "出错:" + ex.Message);
            }
            return "0";
        }

        /// <summary>
        /// 启动CAD
        /// </summary>
        protected bool StartCAD()
        {
            Process[] processCol = Process.GetProcessesByName("acad");
            if (processCol == null || processCol.Length == 0)
            {
                string exePath = "";
                if (!OSHelper.CheckCAD(out exePath))
                {
                    LogHelper.WriteLog("系统检测到您的电脑上未安装 AutoCAD 2008 及以上版本的软件！");
                    return false;
                }
                ProcessStartInfo processInfo = new ProcessStartInfo(exePath);
                processInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                Process cadProcess = Process.Start(processInfo);
                cadProcess.WaitForInputIdle();
            }
            while (CadApplication == null)
            {
                try
                {
                    CadApplication = (IAcadApplication)Marshal.GetActiveObject("AutoCAD.Application");
                    Application.DoEvents();
                    System.Threading.Thread.Sleep(500);
                }
                catch (Exception ex)
                {
                    LogHelper.WriteErrorText(ex);
                }
            }            
            return CadApplication != null;
        }

        /// <summary>
        /// 隐藏cad
        /// </summary>
        protected void SetCadVisible()
        {
            try
            {
                Process[] proCol = Process.GetProcessesByName("acad");
                foreach (Process p in proCol)
                {
                    Form frm = Form.FromHandle(p.MainWindowHandle) as Form;
                    if (frm != null) frm.Visible = true;
                }
            }
            catch
            {
            }
        }

        #endregion

        #region Esri转Cad对象

        /// <summary>
        /// 将Esri点类型转成Cad的块文件
        /// </summary>
        /// <param name="point"></param>
        /// <param name="blockFile"></param>
        /// <param name="angle"></param>
        /// <param name="layerName"></param>
        /// <param name="xscale"></param>
        /// <param name="yscale"></param>
        /// <param name="zscale"></param>
        /// <returns></returns>
        public IAcadBlockReference Point2CadBlock(IPoint point, string blockFile, double angle, string layerName,
            double xscale = 0.5, double yscale = 0.5, double zscale = 1)
        {
            if (!File.Exists(blockFile) || CadDocument == null) return null;
            IAcadBlockReference br = CadDocument.ModelSpace.InsertBlock(new double[] { point.X, point.Y, 0 }, blockFile, xscale, yscale, zscale, angle / 180 * Math.PI, "");
            try
            {
                br.Layer = layerName;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("点转成CAD的块文件" + blockFile + "有误，图层名称" + layerName + "赋值失败：" + ex.Message);
            }         
            return br;
        }

        /// <summary>
        /// 将ArcGIS点转成Cad的点
        /// </summary>
        /// <param name="point">需要转换的点</param>
        /// <param name="layerName">图层名称</param>
        /// <returns></returns>
        public IAcadPoint Point2CadPoint(IPoint point, string layerName)
        {
            if (point == null || point.IsEmpty) return null;
            IAcadPoint pt = CadDocument.ModelSpace.AddPoint(new double[] { point.X, point.Y, 0 });
            try
            {
                pt.Layer = layerName;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("点转成CAD的点有误，图层名称" + layerName + "赋值失败：" + ex.Message);
            }              
            return pt;
        }
        
        /// <summary>
        /// 将ArcGIS的线转成CAD的线
        /// </summary>
        /// <param name="polyline">需要转换的线</param>
        /// <param name="lyrName">图层名称</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadLWPolyline Polyline2CadPolyline(double x1, double y1, double x2, double y2, string lyrName, string style)
        {
            double[] vertex = new double[4];
            vertex[0] = x1;
            vertex[1] = y1;
            vertex[2] = x2;
            vertex[3] = y2;
            IAcadLWPolyline cadPolyline = CadDocument.ModelSpace.AddLightWeightPolyline(vertex);
            try
            {
                cadPolyline.Layer = lyrName;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("线转成CAD的线有误，图层名称" + lyrName + "赋值失败：" + ex.Message);
            }
            try
            {
                if (!string.IsNullOrWhiteSpace(style))
                {
                    CadDocument.Linetypes.Add(style);
                    cadPolyline.Linetype = style;
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("线转成CAD的线有误，样式" + style + "设置失败：" + ex.Message);
            }
            return cadPolyline;
        }
        
        /// <summary>
        /// 将ArcGIS的线转成CAD的线
        /// </summary>
        /// <param name="polyline">需要转换的线</param>
        /// <param name="lyrName">图层名称</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public List<IAcadLWPolyline> Polyline2CadPolyline(IPolyline polyline, string lyrName, string style)
        {
            if (polyline == null || polyline.IsEmpty) return null;
            List<IAcadLWPolyline> cadLines = new List<IAcadLWPolyline>();
            try
            {
                IGeometryCollection geoCol = polyline as IGeometryCollection;
                int count = geoCol.GeometryCount;
                for (int i = 0; i < count; i++)
                {
                    IPointCollection pts = geoCol.get_Geometry(i) as IPointCollection;
                    int ptCount = pts.PointCount;
                    double[] vertex = new double[ptCount * 2];
                    for (int j = 0; j < ptCount; j++)
                    {
                        vertex[j * 2] = pts.get_Point(j).X;
                        vertex[j * 2 + 1] = pts.get_Point(j).Y;
                    }
                    IAcadLWPolyline cadPolyline = CadDocument.ModelSpace.AddLightWeightPolyline(vertex);
                    try
                    {
                        cadPolyline.Layer = lyrName;
                        if (!string.IsNullOrWhiteSpace(style))
                        {
                            CadDocument.Linetypes.Add(style);
                            cadPolyline.Linetype = style;
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteErrorText("线转成CAD的线有误，样式" + style + ",图层名称" + lyrName + "设置失败：" + ex.Message);
                    }
                    cadLines.Add(cadPolyline);
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("线转成CAD的线：" + ex.Message);
            }            
            return cadLines;
        }

        /// <summary>
        /// 将ArcGIS的面转成CAD的线
        /// </summary>
        /// <param name="polygon">需要转换的面</param>
        /// <param name="lyrName">图层名称</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public List<IAcadLWPolyline> Polygon2CadPolyline(IPolygon polygon, string lyrName, string style)
        {
            if (polygon == null || polygon.IsEmpty) return null;
            List<IAcadLWPolyline> cadLines = new List<IAcadLWPolyline>();
            try
            {
                IGeometryCollection geoCol = polygon as IGeometryCollection;
                int count = geoCol.GeometryCount;
                for (int i = 0; i < count; i++)
                {
                    IPointCollection pts = geoCol.get_Geometry(i) as IPointCollection;
                    int ptCount = pts.PointCount;
                    double[] vertex = new double[ptCount * 2];
                    for (int j = 0; j < ptCount; j++)
                    {
                        vertex[j * 2] = pts.get_Point(j).X;
                        vertex[j * 2 + 1] = pts.get_Point(j).Y;
                    }
                    IAcadLWPolyline cadPolyline = CadDocument.ModelSpace.AddLightWeightPolyline(vertex);
                    cadPolyline.Closed = true;
                    try
                    {
                        cadPolyline.Layer = lyrName;
                        if (!string.IsNullOrWhiteSpace(style))
                        {
                            CadDocument.Linetypes.Add(style);
                            cadPolyline.Linetype = style;
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteErrorText("面转成CAD的线有误，样式" + style + ",图层名称" + lyrName + "设置失败：" + ex.Message);
                    }
                    cadLines.Add(cadPolyline);
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorText("面转成CAD的线：" + ex.Message);
            }      
            return cadLines;
        }

        /// <summary>
        /// 将ArcGIS的Ring转成CAD的面
        /// </summary>
        /// <param name="points">要转换的对象</param>
        /// <param name="lyrName">图层名称</param>
        /// <param name="cadColor">颜色</param>
        /// <returns></returns>
        protected AcadRegion Ring2CadRegion(IPointCollection points, string lyrName, AcadAcCmColor cadColor)
        {
            AcadRegion cadRegion = null;
            try
            {
                int ptCount = points.PointCount;
                double[] vertex = new double[ptCount * 2];
                for (int j = 0; j < ptCount; j++)
                {
                    vertex[j * 2] = points.get_Point(j).X;
                    vertex[j * 2 + 1] = points.get_Point(j).Y;
                }
                IAcadLWPolyline cadPolyline = CadDocument.ModelSpace.AddLightWeightPolyline(vertex);
                cadPolyline.Closed = true;
                IAcadLWPolyline[] lines = new IAcadLWPolyline[] { cadPolyline };
                object obj = CadDocument.ModelSpace.AddRegion(lines) as object;
                if (obj is IAcadRegion) cadRegion = obj as AcadRegion;
                else if (obj is object[])
                {
                    object[] objs = obj as object[];
                    if (objs.Length > 0)
                    {
                        object obj2 = objs[0];
                        if (obj2 is IAcadRegion)
                        {
                            cadRegion = obj2 as AcadRegion;
                            try
                            {
                                cadRegion.Layer = lyrName;
                            }
                            catch (Exception ex)
                            {
                                LogHelper.WriteErrorText("Ring转成CAD的面有误，图层名称" + lyrName + "设置失败：" + ex.Message);
                            }                            
                        }
                    }
                }
                cadPolyline.Delete();
                Marshal.ReleaseComObject(cadPolyline);
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("Ring转成CAD的面失败：" + ex.Message);
            }
            return cadRegion;
        }

        /// <summary>
        /// 将ArcGIS的Ring转成CAD的面
        /// </summary>
        /// <param name="ring">要转换的对象</param>
        /// <param name="lyrName">图层名称</param>
        /// <param name="cadColor">颜色</param>
        /// <returns></returns>
        protected AcadRegion Ring2CadRegion(IRing ring, string lyrName, AcadAcCmColor cadColor)
        {
            AcadRegion pCadRegion = null;
            try
            {
                IPointCollection pts = ring as IPointCollection;
                int ptCount = pts.PointCount;
                double[] vertex = new double[ptCount * 2];
                for (int j = 0; j < ptCount; j++)
                {
                    vertex[j * 2] = pts.get_Point(j).X;
                    vertex[j * 2 + 1] = pts.get_Point(j).Y;
                }
                IAcadLWPolyline cadPolyline = CadDocument.ModelSpace.AddLightWeightPolyline(vertex);
                cadPolyline.Closed = true;
                IAcadLWPolyline[] lines = new IAcadLWPolyline[] { cadPolyline };
                object obj = CadDocument.ModelSpace.AddRegion(lines) as object;
                if (obj is IAcadRegion) pCadRegion = obj as AcadRegion;
                else if (obj is object[])
                {
                    object[] objs = obj as object[];
                    if (objs.Length > 0)
                    {
                        object obj2 = objs[0];
                        if (obj2 is IAcadRegion)
                        {
                            pCadRegion = obj2 as AcadRegion;
                            try
                            {
                                pCadRegion.Layer = lyrName;
                            }
                            catch (Exception ex)
                            {
                                LogHelper.WriteErrorText("Ring转成CAD的面有误，图层名称" + lyrName + "设置失败：" + ex.Message);
                            }
                            
                        }
                    }
                }
                cadPolyline.Delete();
                Marshal.ReleaseComObject(cadPolyline);
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("Ring转成CAD的面失败：" + ex.Message);
            }
            return pCadRegion;
        }

        /// <summary>
        /// 将ArcGIS的文本对象转成CAD的文本对象
        /// </summary>
        /// <param name="esriTxt">要转换的对象</param>
        /// <param name="layerName">图层名称</param>
        /// <param name="height">高度</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadText TextElement2CadText(ITextElement esriTxt, string layerName, double height, string style)
        {
            IAcadText cadTxt = null;
            ITextSymbol txtSym = esriTxt.Symbol;
            IElement element = esriTxt as IElement;
            IEnvelope env = element.Geometry.Envelope;
            double[] position = new double[] { env.XMin, env.YMin, 0 };
            try
            {
                cadTxt = CadDocument.ModelSpace.AddText(esriTxt.Text, position, height);
                cadTxt.Rotation = esriTxt.Symbol.Angle * Math.PI / 180;                 
                cadTxt.ScaleFactor = 0.9;
                cadTxt.Alignment = Align2Alignment(txtSym.HorizontalAlignment, txtSym.VerticalAlignment);  
                try
                {
                    cadTxt.Layer = layerName;
                    if (string.IsNullOrEmpty(style)) cadTxt.StyleName = "Standard";
                    else cadTxt.StyleName = style;
                    cadTxt.TextAlignmentPoint = position;
                }
                catch (System.Exception ex)
                {
                    LogHelper.WriteErrorText(esriTxt.Text + "的字体符号" + style + "不存在：" + ex.Message);
                }
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("创建文本注记" + esriTxt.Text + "失败：" + ex.Message);
            }
            return cadTxt;
        }

        /// <summary>
        /// 将ArcGIS的文本对象转成CAD的文本对象
        /// </summary>
        /// <param name="esriTxt">要转换的对象</param>
        /// <param name="xmin">位置：x坐标</param>
        /// <param name="ymin">位置：y坐标</param>
        /// <param name="layerName">图层名称</param>
        /// <param name="height">高度</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadText TextElement2CadText(ITextElement esriTxt, double xmin, double ymin, string layerName, double height, string style)
        {
            IAcadText cadTxt = null;
            IElement element = esriTxt as IElement;
            double[] dblPosition = new double[] { xmin, ymin, 0 };
            try
            {
                cadTxt = CadDocument.ModelSpace.AddText(esriTxt.Text, dblPosition, height);
                cadTxt.Rotation = esriTxt.Symbol.Angle * Math.PI / 180;
                cadTxt.Alignment = Align2Alignment(esriTxt.Symbol.HorizontalAlignment, esriTxt.Symbol.VerticalAlignment);
                try
                {
                    cadTxt.Layer = layerName;
                    cadTxt.TextAlignmentPoint = dblPosition;
                    if (string.IsNullOrEmpty(style)) cadTxt.StyleName = "Standard";
                    else cadTxt.StyleName = style;
                }
                catch (System.Exception ex)
                {
                    LogHelper.WriteErrorText(esriTxt.Text + "的字体符号" + style + "不存在：" + ex.Message);
                }                
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("创建文本注记" + esriTxt.Text + "失败：" + ex.Message);
            }
            return cadTxt;
        }

        /// <summary>
        /// 将文本对象转成CAD的文本对象
        /// </summary>
        /// <param name="txt">要转换的对象</param>
        /// <param name="point">位置</param>
        /// <param name="align"></param>
        /// <param name="layerName">图层名称</param>
        /// <param name="height">高度</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadText Text2CadText(string txt, IPoint point, AcAlignment align, string layerName, double height, string style)
        {
            return Text2CadText(txt, point.X, point.Y, align, layerName, height, style);
        }

        /// <summary>
        /// 将文本对象转成CAD的文本对象
        /// </summary>
        /// <param name="value">要转换的对象</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="align"></param>
        /// <param name="layerName">图层名称</param>
        /// <param name="height">高度</param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadText Text2CadText(string value, double x,double y, AcAlignment align, string layerName, double height, string style)
        {
            double[] position = new double[] { x, y, 0 };
            IAcadText cadTxt = null; 
            try
            {
                cadTxt = CadDocument.ModelSpace.AddText(value, position, height);
                cadTxt.Rotation = 0;
                cadTxt.Alignment = align;
                cadTxt.Layer = layerName;
                try
                {
                    if (string.IsNullOrEmpty(style)) cadTxt.StyleName = "Standard";
                    else cadTxt.StyleName = style;
                    cadTxt.TextAlignmentPoint = position;
                }
                catch (System.Exception ex)
                {
                    LogHelper.WriteErrorText(value + "的字体符号" + style + "不存在：" + ex.Message);
                }
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("创建文本注记" + value + "失败：" + ex.Message);
            }
            return cadTxt;
        }

        /// <summary>
        /// 将文本对象转成CAD的文本对象
        /// </summary>
        /// <param name="value">要转换的对象</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="align"></param>
        /// <param name="layerName">图层名称</param>
        /// <param name="width">宽度</param>
        /// <param name="height"></param>
        /// <param name="style">样式</param>
        /// <returns></returns>
        public IAcadMText Text2CadMText(string value, double x, double y, AcAttachmentPoint align, string layerName, double width, double height, string style)
        {
            double[] position = new double[] { x, y, 0 };
            IAcadMText cadTxt = null;
            try
            {
                cadTxt = CadDocument.ModelSpace.AddMText(position, width, value);
                cadTxt.Height = height;
                //cadTxt.Rotation = 0;
                cadTxt.AttachmentPoint = align;
                cadTxt.Layer = layerName;
                //cadTxt.AttachmentPoint = AcAttachmentPoint.acAttachmentPointBottomRight;
                try
                {
                    if (string.IsNullOrEmpty(style)) cadTxt.StyleName = "Standard";
                    else cadTxt.StyleName = style;
                    //cadTxt.
                    //cadTxt.TextAlignmentPoint = position;
                }
                catch (System.Exception ex)
                {
                    LogHelper.WriteErrorText(value + "的字体符号" + style + "不存在：" + ex.Message);
                }
            }
            catch (System.Exception ex)
            {
                LogHelper.WriteErrorText("创建文本注记" + value + "失败：" + ex.Message);
            }
            return cadTxt;
        }

        /// <summary>
        /// 将Esri对齐方式转成Cad的对齐方式
        /// </summary>
        /// <param name="horizontal">水平对齐</param>
        /// <param name="vertical">垂直对象</param>
        /// <returns></returns>
        public AcAlignment Align2Alignment(esriTextHorizontalAlignment horizontal, esriTextVerticalAlignment vertical)
        {
            switch (horizontal)
            {
                case esriTextHorizontalAlignment.esriTHACenter:
                    if (vertical == esriTextVerticalAlignment.esriTVABaseline) return AcAlignment.acAlignmentBottomCenter;
                    else if (vertical == esriTextVerticalAlignment.esriTVABottom) return AcAlignment.acAlignmentBottomCenter;
                    else if (vertical == esriTextVerticalAlignment.esriTVACenter) return AcAlignment.acAlignmentMiddleCenter;
                    else return AcAlignment.acAlignmentTopCenter;
                case esriTextHorizontalAlignment.esriTHALeft:
                    if (vertical == esriTextVerticalAlignment.esriTVABaseline) return AcAlignment.acAlignmentLeft;
                    else if (vertical == esriTextVerticalAlignment.esriTVABottom) return AcAlignment.acAlignmentBottomLeft;
                    else if (vertical == esriTextVerticalAlignment.esriTVACenter) return AcAlignment.acAlignmentMiddleLeft;
                    else return AcAlignment.acAlignmentTopLeft;
                case esriTextHorizontalAlignment.esriTHARight:
                    if (vertical == esriTextVerticalAlignment.esriTVABaseline) return AcAlignment.acAlignmentRight;
                    else if (vertical == esriTextVerticalAlignment.esriTVABottom) return AcAlignment.acAlignmentBottomRight;
                    else if (vertical == esriTextVerticalAlignment.esriTVACenter) return AcAlignment.acAlignmentMiddleRight;
                    else return AcAlignment.acAlignmentTopRight;
                case esriTextHorizontalAlignment.esriTHAFull:
                    if (vertical == esriTextVerticalAlignment.esriTVABaseline) return AcAlignment.acAlignmentAligned;
                    else if (vertical == esriTextVerticalAlignment.esriTVABottom) return AcAlignment.acAlignmentFit;
                    else if (vertical == esriTextVerticalAlignment.esriTVACenter) return AcAlignment.acAlignmentCenter;
                    else return AcAlignment.acAlignmentMiddle;
            }
            return AcAlignment.acAlignmentBottomLeft;
        }
        #endregion

    }
}
